package server

import (
	"context"
	"encoding/json"
	"errors"
	"log"
	"sync"
	"time"

	"github.com/coder/websocket"
	"github.com/xkgo/lcs/binary"
)

// MessageType 消息类型
type MessageType string

const (
	// TextMessage 文本消息
	TextMessage MessageType = "text"
	// SubscribeMessage 订阅消息
	SubscribeMessage MessageType = "subscribe"
	// UnsubscribeMessage 取消订阅消息
	UnsubscribeMessage MessageType = "unsubscribe"
	// PingMessage 心跳请求消息
	PingMessage MessageType = "ping"
	// PongMessage 心跳响应消息
	PongMessage MessageType = "pong"
)

// Message 消息结构
type Message struct {
	// 消息类型
	Type MessageType `json:"type"`
	// 消息内容
	Content string `json:"content,omitempty"`
	// 主题（用于订阅/取消订阅）
	Topic string `json:"topic,omitempty"`
}

// Client 表示一个WebSocket客户端连接
type Client struct {
	// 客户端唯一标识
	ID string
	// WebSocket连接
	conn *websocket.Conn
	// 服务器引用
	server *Server
	// 发送消息的通道
	send chan []byte
	// 关闭标志
	closed bool
	// 互斥锁，保护closed字段
	closeMu sync.Mutex
	// 关闭通知通道
	done chan struct{}
}

// NewClient 创建一个新的客户端
func NewClient(id string, conn *websocket.Conn, server *Server) *Client {
	return &Client{
		ID:     id,
		conn:   conn,
		server: server,
		send:   make(chan []byte, 256),
		done:   make(chan struct{}),
	}
}

// Start 开始处理客户端连接
func (c *Client) Start() {
	// 启动读写goroutine
	go c.readPump()
	go c.writePump()
}

// readPump 从WebSocket连接读取消息
func (c *Client) readPump() {
	defer func() {
		c.Close()
	}()

	for {
		select {
		case <-c.done:
			return
		default:
			// 设置读取超时
			ctx, cancel := context.WithTimeout(context.Background(), c.server.options.ReadTimeout)
			_, data, err := c.conn.Read(ctx)
			cancel()

			if err != nil {
				if websocket.CloseStatus(err) != websocket.StatusNormalClosure {
					log.Printf("Error reading from client %s: %v", c.ID, err)
				}
				return
			}

			// 处理接收到的消息
			c.handleMessage(data)
		}
	}
}

// writePump 向WebSocket连接写入消息
func (c *Client) writePump() {
	defer func() {
		c.Close()
	}()

	for {
		select {
		case message, ok := <-c.send:
			if !ok {
				// 通道已关闭
				return
			}

			// 设置写入超时
			ctx, cancel := context.WithTimeout(context.Background(), c.server.options.WriteTimeout)
			err := c.conn.Write(ctx, websocket.MessageText, message)
			cancel()

			if err != nil {
				log.Printf("Error writing to client %s: %v", c.ID, err)
				return
			}
		case <-c.done:
			return
		}
	}
}

// handleMessage 处理接收到的消息
func (c *Client) handleMessage(data []byte) {
	// 尝试解析为二进制消息
	binMsg, err := binary.TryDecode(data)
	if err == nil {
		// 成功解析为二进制消息
		switch binMsg.Type {
		case binary.SubscribeMessage:
			// 处理订阅请求
			if binMsg.Topic != "" {
				c.server.Subscribe(c.ID, binMsg.Topic)
			}
		case binary.UnsubscribeMessage:
			// 处理取消订阅请求
			if binMsg.Topic != "" {
				c.server.Unsubscribe(c.ID, binMsg.Topic)
			}
		case binary.TextMessage:
			// 处理文本消息
			log.Printf("Received binary text message from client %s: %s", c.ID, binMsg.Content)
		case binary.PingMessage:
			// 处理心跳请求
			log.Printf("Received binary ping from client %s", c.ID)
			// 发送pong响应
			pongMsg := binary.NewPongMessage()
			pongData, err := pongMsg.Encode()
			if err != nil {
				log.Printf("Error encoding binary pong message: %v", err)
				return
			}
			if err := c.Send(pongData); err != nil {
				log.Printf("Error sending binary pong to client %s: %v", c.ID, err)
			}
		case binary.PongMessage:
			// 处理心跳响应，通常只需要记录即可
			log.Printf("Received binary pong from client %s", c.ID)
		default:
			log.Printf("Unknown binary message type from client %s: %d", c.ID, binMsg.Type)
		}
		return
	}

	// 如果不是二进制消息，尝试解析为JSON消息（兼容旧版本）
	var msg Message
	if err := json.Unmarshal(data, &msg); err != nil {
		log.Printf("Error unmarshaling message from client %s: %v", c.ID, err)
		return
	}

	switch msg.Type {
	case SubscribeMessage:
		// 处理订阅请求
		if msg.Topic != "" {
			c.server.Subscribe(c.ID, msg.Topic)
		}
	case UnsubscribeMessage:
		// 处理取消订阅请求
		if msg.Topic != "" {
			c.server.Unsubscribe(c.ID, msg.Topic)
		}
	case TextMessage:
		// 处理文本消息，这里可以根据需要扩展
		log.Printf("Received JSON text message from client %s: %s", c.ID, msg.Content)
	case PingMessage:
		// 处理心跳请求
		log.Printf("Received ping from client %s", c.ID)
		// 发送pong响应
		pongMsg := Message{Type: PongMessage}
		pongData, err := json.Marshal(pongMsg)
		if err != nil {
			log.Printf("Error marshaling pong message: %v", err)
			return
		}
		if err := c.Send(pongData); err != nil {
			log.Printf("Error sending pong to client %s: %v", c.ID, err)
		}
	case PongMessage:
		// 处理心跳响应，通常只需要记录即可
		log.Printf("Received pong from client %s", c.ID)
	default:
		log.Printf("Unknown JSON message type from client %s: %s", c.ID, msg.Type)
	}
}

// Send 向客户端发送消息
func (c *Client) Send(message []byte) error {
	c.closeMu.Lock()
	closed := c.closed
	c.closeMu.Unlock()

	if closed {
		return errors.New("client connection closed")
	}

	select {
	case c.send <- message:
		return nil
	case <-time.After(time.Second):
		return errors.New("send timeout")
	}
}

// Close 关闭客户端连接
func (c *Client) Close() {
	c.closeMu.Lock()
	if c.closed {
		c.closeMu.Unlock()
		return
	}
	c.closed = true
	c.closeMu.Unlock()

	// 关闭WebSocket连接
	c.conn.Close(websocket.StatusNormalClosure, "connection closed")

	// 关闭通道
	close(c.done)
	close(c.send)

	// 从服务器中注销
	c.server.unregisterClient(c.ID)
}
